home *** CD-ROM | disk | FTP | other *** search
/ Celestin Apprentice 7 / Apprentice-Release7.iso / Source Code / PowerPlant / UGetMultipleFiles 1.4 / UGetMultipleFiles.cp < prev    next >
Encoding:
Text File  |  1997-07-24  |  39.4 KB  |  1,067 lines  |  [TEXT/CWIE]

  1. // --------------------------------------------------------------------------------------------
  2. //    UGetMulltipleFiles - An Add Files utility class
  3. //        By David Hirsch
  4. //            please read the "Read Me" file.
  5. //
  6. //    Notes:
  7. //        1)    The List Manager's list that we display in the dialog box starts with zero for the
  8. //            first item, while the LArray that holds the FSSpec records starts with 1.
  9. //
  10. // --------------------------------------------------------------------------------------------
  11.  
  12. #include "UGetMultipleFiles.h"
  13. #include <string.h>
  14. #include <LString.h>
  15. #include "GetDirItems.h"
  16. #include <UDrawingState.h>
  17. #include "patch.h"
  18.  
  19. // --------------------------------------------------------------------------------------------
  20. //     Global variables
  21. // --------------------------------------------------------------------------------------------
  22. ListHandle             gToolboxList = nil;
  23. UniversalProcPtr    patchCode = nil;
  24. Boolean             needToFixButtons;    // this is so we can delay one "cycle"
  25. Boolean                tabKludge = false;
  26.  
  27. // --------------------------------------------------------------------------------------------
  28. //     Static class variables
  29. // --------------------------------------------------------------------------------------------
  30.  
  31. ListHandle            UGetMultipleFiles::sAddedListH = nil;
  32. DialogPtr            UGetMultipleFiles::sDialogP = nil;
  33. StandardFileReply    UGetMultipleFiles::sReply;
  34. LArray                *UGetMultipleFiles::sTheFSpecs = nil;
  35. LArray                *UGetMultipleFiles::sNixFiles = nil;
  36. short                UGetMultipleFiles::sNumTypes = -1;
  37. SFTypeList            UGetMultipleFiles::sTypeList = {};
  38. LFSSpecArrayComp    *UGetMultipleFiles::sComparatorP = nil;
  39. Boolean                UGetMultipleFiles::sNeedToRebuild = false;
  40. Boolean                UGetMultipleFiles::sAllowConversion = true;
  41. Boolean                UGetMultipleFiles::sShowFolders = true;
  42. Boolean                UGetMultipleFiles::sAddFolders = false;
  43. short                UGetMultipleFiles::sKeyPressed = 0;
  44.  
  45. // --------------------------------------------------------------------------------------------
  46. //    UGetMultipleFiles
  47. //         Default constructor; calls detailed constructor, but without a prompt or typelist.
  48. // --------------------------------------------------------------------------------------------
  49. UGetMultipleFiles::UGetMultipleFiles()
  50. {
  51.     UGetMultipleFiles((unsigned char *)"", (short) -1, (unsigned long *) nil, nil, false, true, false);
  52. }
  53.  
  54. // --------------------------------------------------------------------------------------------
  55. //    UGetMultipleFiles - main constructor.
  56. // --------------------------------------------------------------------------------------------
  57. UGetMultipleFiles::UGetMultipleFiles(Str255 prompt, short numTypes,
  58.                                     SFTypeList typeList, LArray *inFileList,
  59.                                     Boolean inAllowConversion, Boolean inShowFolders,
  60.                                     Boolean inAddFolders)
  61. {
  62.     Point                myPoint = {-1, -1};                // have dialog appear centered
  63.     FileFilterYDUPP        theFileFilterYDUPP;
  64.     DlgHookYDUPP        theDlgHookYDUPP;
  65.     ModalFilterYDUPP    theModalFilterYDUPP;
  66.     ActivateYDUPP        theActivateYDUPP;
  67.     PatchRouterUPP        thePatchRouterUPP;
  68.     FSSpec                oneFSSpec;
  69.     Int16                activateListArray[3];
  70.     Handle                patchResource;
  71.     dataHolderType        myData;
  72.     
  73.     if (sComparatorP == nil)
  74.         sComparatorP = new LFSSpecArrayComp();    // create comparator for the FSSpec array
  75.     if (sTheFSpecs == nil)
  76.         sTheFSpecs = new LArray(sizeof(FSSpec), sComparatorP);    // create the FSSpec array
  77.     sTheFSpecs->SetKeepSorted(true);            // sort the array
  78.  
  79.     myData.whichFocused = kUserItem_FileList;        // for focus routines; begins focused on Toolbox List
  80.     sNixFiles = inFileList;
  81.     if ((sNixFiles != nil))
  82.         sNixFiles->SetComparator(sComparatorP);
  83.  
  84.     sNumTypes = numTypes;                        // store the numTypes parameter
  85.     for (short j=0; j <= numTypes - 1; j++)            // store each type
  86.         sTypeList[j] = typeList[j];
  87.     
  88.     LString::CopyPStr((unsigned char *)prompt, (unsigned char *)oneFSSpec.name);        // send in the prompt disguised as the first FSSpec
  89.     sTheFSpecs->InsertItemsAt(1, (sTheFSpecs->GetCount()) + 1, &oneFSSpec);
  90.     
  91.     theDlgHookYDUPP = NewDlgHookYDProc(myDialogHook);
  92.     theModalFilterYDUPP = NewModalFilterYDProc(myModalFilter);
  93.     theFileFilterYDUPP = NewFileFilterYDProc(myFileFilter);
  94.     theActivateYDUPP = NewActivateYDProc(myActivateProc);
  95.     activateListArray[0] = 2;
  96.     activateListArray[1] = kUserItem_FileList;
  97.     activateListArray[2] = kUserItem_AddedList;
  98. //    UDesktop::Deactivate();    //DMH 4.9.97
  99.     ::InitCursor();            //DMH 4.9.97
  100.     sAllowConversion = inAllowConversion;
  101.     sShowFolders = inShowFolders;
  102.     sAddFolders = inAddFolders;
  103.  
  104.  
  105.     patchResource = GetResource('patc', 0);
  106.     if (patchResource) {
  107.         HLock(patchResource);
  108.         HNoPurge(patchResource);
  109.         
  110.         thePatchRouterUPP = NewPatchRouterProc((ProcPtr)*patchResource);
  111.         CallPatchRouterProc(thePatchRouterUPP, kInstall, &gToolboxList);
  112.  
  113.         gToolboxList = nil;
  114.         ::CustomGetFile(theFileFilterYDUPP, -1, NULL, &sReply, kDLOG_OpenMultiple, myPoint,
  115.             theDlgHookYDUPP, theModalFilterYDUPP, activateListArray, theActivateYDUPP, (void *) &myData);
  116.         gToolboxList = nil;
  117.  
  118.         CallPatchRouterProc(thePatchRouterUPP, kRemove, &gToolboxList);
  119.         DisposeRoutineDescriptor(thePatchRouterUPP);
  120.     }
  121.     else {
  122.         SysBeep(1);
  123.     }
  124. //    UDesktop::Activate();    //DMH 4.9.97
  125.     DisposeRoutineDescriptor(theDlgHookYDUPP);
  126.     DisposeRoutineDescriptor(theModalFilterYDUPP);
  127.     DisposeRoutineDescriptor(theActivateYDUPP);
  128.     DisposeRoutineDescriptor(theFileFilterYDUPP);
  129.  
  130.         // If we weren't allowing conversion/checking, and we got a cancel message, but
  131.         // we never deleted the files, then what really happened was that the user
  132.         // in fact clicked the Done button, and the Dialog Hook sent the cancel message
  133.         // to get around the conversion/checking stuff.  Reset the reply field.
  134.     if ((!sAllowConversion) && !(sReply.sfGood) && (sTheFSpecs->GetCount() > 0)) {
  135.         sReply.sfGood = true;
  136.     }
  137.  
  138. //    Kludge for NOW Direct Open feature of SuperBoomerang - Direct Open does not 
  139. //    fill in parID and vRefNum in the dialog hook which UGetMultipleFiles relies
  140. //    on to work its magic.  Fortunately, Direct Open does fill in all FSSpec
  141. //    fields in the reply record.  if there is only one FSSpec in the mTheFSpecs array
  142. //    and its parID and vRefNum are both 0, replace it with the FSSpec in the reply record.
  143.     if (sTheFSpecs->GetCount() == 1)  {
  144.         sTheFSpecs->FetchItemAt(1, &oneFSSpec);
  145.         if ( (0 == oneFSSpec.parID) && (0 == oneFSSpec.vRefNum) )  {
  146.             sTheFSpecs->RemoveItemsAt(1, 1); // remove the incomplete FSSpec
  147.             sTheFSpecs->InsertItemsAt(1, 1, &sReply.sfFile);
  148.         }
  149.     }
  150. }
  151.  
  152. // --------------------------------------------------------------------------------------------
  153. //    UGetMultipleFiles - main destructor.
  154. // --------------------------------------------------------------------------------------------
  155. UGetMultipleFiles::~UGetMultipleFiles()
  156. {
  157.     if (sComparatorP != nil) {
  158.         delete sComparatorP;
  159.         sComparatorP = nil;
  160.     }
  161.     if (sTheFSpecs != nil) {
  162.         delete sTheFSpecs;
  163.         sTheFSpecs = nil;
  164.     }
  165. }
  166.  
  167. // --------------------------------------------------------------------------------------------
  168. //    GetFSSpecs - accessor for the FSSpec LArray.
  169. // --------------------------------------------------------------------------------------------
  170. LArray* UGetMultipleFiles::GetFSSpecs()
  171. {
  172.     return sTheFSpecs;
  173. }
  174.  
  175. // --------------------------------------------------------------------------------------------
  176. //    GetSFReply - accessor for the reply record.
  177. // --------------------------------------------------------------------------------------------
  178. StandardFileReply UGetMultipleFiles::GetSFReply()
  179. {
  180.     return sReply;
  181. }
  182.  
  183.  
  184. // --------------------------------------------------------------------------------------------
  185. //    myModalFilter - Modal Dialog calls this just after the Standard File Package's filter
  186. //         This routine was taken from the Apple Macintosh Developer Technical Support
  187. //            Standard File Application, which I downloaded from Apple's web site, and modified.
  188. // --------------------------------------------------------------------------------------------
  189. pascal Boolean UGetMultipleFiles::myModalFilter (DialogPtr theDialogP, EventRecord *event,
  190.                                                  short *itemHit, void *ioParam)
  191. {
  192.  
  193.     Rect            itemRect;
  194.     Point            clickPos;
  195.     Handle            itemHandle;
  196.     Boolean            firstTimeThrough = true;
  197.  
  198.     if (GetWRefCon(theDialogP) != sfMainDialogRefCon)
  199.             return false;
  200.     if ((*event).what == mouseDown) {
  201.         clickPos = (*event).where;
  202.         GlobalToLocal(&clickPos);
  203.         GetDialogItem(theDialogP, kUserItem_AddedList, userItem, &itemHandle, &itemRect);
  204.         if (PtInRect(clickPos, &itemRect)) {
  205.             if (::LClick(clickPos, (*event).modifiers, sAddedListH)) {
  206.                 RemoveFromList();
  207.                 sNeedToRebuild = true;
  208.             }
  209.             *itemHit = kUserItem_AddedList;
  210.             return true;
  211.         }
  212.     }
  213.     if ((*event).what == keyDown) {
  214.         Int16    theKey = (*event).message & charCodeMask;
  215.         if (theKey == '\t') {
  216.             if ((((dataHolderType *)ioParam)->whichFocused) == kUserItem_AddedList)
  217.                 ((dataHolderType *)ioParam)->whichFocused = kUserItem_FileList;
  218.             else
  219.                 ((dataHolderType *)ioParam)->whichFocused = kUserItem_AddedList;
  220.         }
  221.         if (theKey == kHome ||
  222.             theKey == kEnd ||
  223.             theKey == kUpArrow ||
  224.             theKey == kDownArrow ||
  225.             theKey == kPageUp ||
  226.             theKey == kPageDown) {
  227.             sKeyPressed = theKey;
  228.         }
  229.     }
  230.     return false;
  231. }
  232.  
  233. // --------------------------------------------------------------------------------------------
  234. //    myFileFilter - This dictates which files to show in the list
  235. // --------------------------------------------------------------------------------------------
  236. pascal Boolean UGetMultipleFiles::myFileFilter (CInfoPBPtr pb, void *ioParam)
  237. {
  238. #pragma unused(ioParam)
  239.  
  240.     // extract the FSSpec from the CInfoPBPtr
  241.     FSSpec tempSpec;
  242.     LStr255 nameStr(pb->hFileInfo.ioNamePtr);
  243.     UInt8 lastNamePos = nameStr.ReverseFind(':');
  244.     nameStr.Remove(1, lastNamePos);
  245.     OSErr theErr = ::FSMakeFSSpec(pb->hFileInfo.ioVRefNum, pb->hFileInfo.ioFlParID, nameStr, &tempSpec);
  246.     if (theErr != noErr)
  247.         return false;
  248.  
  249.     if (!sAddFolders) {    //    Don't need this if adding folders, since we're treating them like files!
  250.         if (sShowFolders && IsFolder(pb)) {    // if we're showing folders and this is a folder
  251.             return false;    // then show it
  252.         }
  253.     }
  254.  
  255.     if (!IsFolder(pb) && !TypeInList( pb->hFileInfo.ioFlFndrInfo.fdType )) {    // if it's not a folder and the type isn't in the list
  256.         return true;    // then don't show it
  257.     }    // Note: we need this check for when we're adding folders and showing them
  258.     
  259.     // see if it's in the list of added files
  260.     if (sTheFSpecs->FetchIndexOf(&tempSpec) != sTheFSpecs->index_Bad)    // if it is in the added list
  261.         return true;    // then don't show it
  262.     else {
  263.         if (sNixFiles == nil)    // don't suppress any
  264.             return false;
  265.         // see if it's in the list of excluded files
  266.         else if (sNixFiles->FetchIndexOf(&tempSpec) != sNixFiles->index_Bad)    // if it is in the exclude list
  267.             return true;            // then don't show it
  268.         else
  269.             return false;
  270.     }
  271. }
  272.  
  273. // --------------------------------------------------------------------------------------------
  274. //    myActivateProc - Modal Dialog calls this when activating or deactivating a list box
  275. // --------------------------------------------------------------------------------------------
  276. pascal void UGetMultipleFiles::myActivateProc (DialogPtr theDialogP, short itemNum,
  277.                                                  Boolean activating, void *ioParam)
  278. {
  279.     dataHolderType    *myData = (dataHolderType *) ioParam;
  280.     StColorPortState theState(theDialogP);
  281.     RGBColor    theBackColor;
  282.     Rect        itemRect;
  283.     short        itemKind;
  284.  
  285.     if (GetWRefCon(theDialogP) != sfMainDialogRefCon)
  286.         return;
  287.     ::PenNormal();
  288.     ::PenSize(2, 2);
  289.     if (itemNum == kUserItem_AddedList) {
  290.         GetDialogItem(theDialogP, itemNum, &itemKind, userItem, &itemRect);
  291.         ::InsetRect(&itemRect, -4, -4);
  292.         if (activating) {
  293.             ::ForeColor(blackColor);
  294.             ::FrameRect(&itemRect);
  295.             ::LActivate(true, sAddedListH);
  296.             EnsureSelection(kUserItem_AddedList);
  297.             myData->whichFocused = kUserItem_AddedList;
  298.         } else {
  299.             ::LActivate(false, sAddedListH);
  300.             ::PenNormal();
  301.             ::PenSize(2, 2);
  302.             ::GetBackColor(&theBackColor);
  303.             ::RGBForeColor(&theBackColor);
  304.             ::FrameRect(&itemRect);
  305.             myData->whichFocused = kUserItem_FileList;
  306.         }
  307.     } else {
  308.         if (activating) {
  309.             EnsureSelection(kUserItem_FileList);
  310.             tabKludge = true;
  311.         }
  312.     }
  313.     needToFixButtons = true;
  314. }
  315.  
  316. #include <stdlib.h>
  317. // --------------------------------------------------------------------------------------------
  318. //    myDialogHook - the Standard File Package calls this routine just after myModalFilter.
  319. // --------------------------------------------------------------------------------------------
  320. pascal short UGetMultipleFiles::myDialogHook (short item, DialogPtr theDialogP, void *ioParam)
  321. {
  322.     Cell            curSelCell = {0,0};
  323.     Cell            nextCell;
  324.     Boolean            result;
  325.     dataHolderType    *myData = (dataHolderType *) ioParam;
  326.     static Boolean     onceThrough;
  327.     static Boolean    haveRebuilt = false;
  328.     Rect            itemRect;
  329.     Handle            itemHandle;
  330.     short            itemKind;
  331.     FSSpec            oneFSSpec;
  332.     UserItemUPP        theDLUserItemUPP = nil;
  333.     
  334.     if (GetWRefCon(theDialogP) != sfMainDialogRefCon)
  335.            return item;
  336.  
  337.     switch (item) {
  338.         case sfHookFirstCall:    // gets called just before the dialog is made
  339.             onceThrough = false;
  340.             needToFixButtons = true;
  341.             ListHandle temp = gToolboxList;    // save the Toolbox ListHandle
  342.             sAddedListH = MyCreateTextListInDialog(theDialogP, kUserItem_AddedList); // make the added list
  343.             gToolboxList = temp;    // replace the Toolbox LH that was overwritten by our call of LNew
  344.             GetDialogItem(theDialogP, kUserItem_AddedList, userItem, &itemHandle, &itemRect);
  345.             theDLUserItemUPP = NewUserItemProc(&(MyDrawListItem));        // install the draw routine for
  346.                                                                                             //    the list of added files
  347.             SetDialogItem(theDialogP, kUserItem_AddedList, userItem, (Handle)theDLUserItemUPP, &itemRect);
  348.  
  349.             sDialogP = theDialogP;                            // store the dialog pointer
  350.             if (!sAddFolders) {
  351.                 HideDialogItem(sDialogP, kStdButton_Select); 
  352.                 GetDialogItem(sDialogP, kStdButton_Select, btnCtrl, &itemHandle, &itemRect);
  353.                 ::SizeWindow(sDialogP, sDialogP->portRect.right - sDialogP->portRect.left,
  354.                     sDialogP->portRect.bottom - sDialogP->portRect.top - (itemRect.bottom - itemRect.top), true);
  355.             }
  356.             SetEnable(sfItemOpenButton, true);    // Add button is active, no highlighting
  357.             SetEnable(kStdButton_Done, true);    // Done button is active, no highlighting
  358.             SetEnable(kStdButton_Remove, false);    // Remove button is inactive
  359.             SetEnable(kStdButton_RemoveAll, false);    // Remove all button is inactive
  360.  
  361.             SetEnable(kStdButton_AddAll, true);    // Add all button is active, no highlighting
  362.             sTheFSpecs->FetchItemAt(1, &oneFSSpec);            // Extract the prompt from the first FSSpec
  363.             sTheFSpecs->RemoveItemsAt(1, 1);
  364.             GetDialogItem(theDialogP, kStaticText_Prompt, &itemKind, &itemHandle, &itemRect);
  365.             SetDialogItemText(itemHandle, oneFSSpec.name);
  366.             
  367.             onceThrough = true;
  368.  
  369.             return sfHookNullEvent;
  370.         break;
  371.         case sfHookLastCall:    // called just after we're done with the dialog
  372.             DisposeRoutineDescriptor(theDLUserItemUPP);
  373.             LDispose(sAddedListH);
  374.             if ((sTheFSpecs->GetCount() > 0) && (!sAllowConversion)) {
  375.                     // since we're supressing checking, we won't be sending an sfItemOpenButton
  376.                     // "message", that would cause the Standard File Package to reset the current
  377.                     // volume and directory to that contained in the sReply.sfFile field.
  378.                     // We'll therefore set it by hand.
  379.                 FSSpec *oneSpec = (FSSpec *) sTheFSpecs->GetItemPtr(1);
  380.                 SetCurrentVolume(oneSpec->vRefNum);
  381.                 SetCurrentDirectory(oneSpec->parID);
  382.             }
  383.             return sfHookNullEvent;
  384.         break;
  385.         case sfItemCancelButton:
  386.             sTheFSpecs->RemoveItemsAt(sTheFSpecs->GetCount(), 1);    // remove all files from array
  387.             FixButtons(theDialogP, ioParam);
  388.             return sfItemCancelButton;
  389.         break;
  390.         
  391.         case kUpArrowPlusCO:
  392.         case kDownArrowPlusCO:
  393.             needToFixButtons = true;
  394.         break;
  395.         
  396.         case sfHookGoToDesktop:
  397.         case sfHookFolderPopUp:
  398.         case sfItemVolumeUser:
  399.         case sfHookOpenFolder:
  400.             sNeedToRebuild = true;
  401.             needToFixButtons = true;
  402.         break;
  403.  
  404.         case kUserItem_FileList:
  405.             FixButtons(theDialogP, ioParam);
  406.             return sfHookSetActiveOffset + kUserItem_FileList;
  407.         break;
  408.             
  409.         case sfItemOpenButton:
  410.         case kStdButton_Select:
  411.             AddOneToList(sReply.sfFile.name, sReply.sfFile.parID, sReply.sfFile.vRefNum);
  412.             result = LGetSelect(true, &curSelCell, gToolboxList);
  413.             if (result) {    // if there was something selected
  414.                 Boolean isLast = (curSelCell.v == (**gToolboxList).dataBounds.bottom - 1);
  415.                 if (sReply.sfFile.parID != fsRtParID) { // and it wasn't a volume
  416.                     LDelRow(1, curSelCell.v, gToolboxList);    // delete that cell
  417.                 } else {    // it was a volume, so we didn't delete it - increment the selection instead
  418.                     LSetSelect(false, curSelCell, gToolboxList);
  419.                     curSelCell.v++;
  420.                 }
  421.                 if (isLast)    // if the cell was the last one
  422.                     nextCell.v = (**gToolboxList).dataBounds.bottom - 1;
  423.                 else        // else it was not the last cell
  424.                     nextCell.v = curSelCell.v;
  425.  
  426.                 nextCell.h = 0;
  427.                 LSetSelect(true, nextCell, gToolboxList);
  428.                 LAutoScroll(gToolboxList);
  429.             }            
  430.             needToFixButtons = true;    // if we FixButtons now, the Reply field will not have been changed yet.
  431.                                         // this will delay things by one cycle.
  432.             return sfHookNullEvent;
  433.         break;
  434.         
  435.         case kStdButton_Done:
  436.             if (sAllowConversion)
  437.                 return sfItemOpenButton;    // nothing to do here; everything is in the FSSpec array
  438.             else
  439.                 return sfItemCancelButton;    // since we're supressing checking, we fake a cancel messge
  440.                                                     // we'll know in the calling function that it was fake
  441.                                                     // because there will still be files in the array.
  442.         break;
  443.         case kStdButton_Remove:
  444.             RemoveFromList(); 
  445.             needToFixButtons = true;
  446.             haveRebuilt = true;
  447.             return sfHookRebuildList;
  448.         break;
  449.         case kStdButton_AddAll:
  450.             HLock((Handle)sAddedListH);
  451.             AddAllToList(!GetEnabled(kDesktopButton));    // if Desktop btn is enabled, we're not at the Desktop level
  452.             HUnlock((Handle)sAddedListH);
  453.             needToFixButtons = true;
  454.             haveRebuilt = true;
  455.             return sfHookRebuildList;
  456.         break;
  457.         case kStdButton_RemoveAll:
  458.             RemoveAllFromList();
  459.             needToFixButtons = true;
  460.             haveRebuilt = true;
  461.             return sfHookRebuildList;
  462.         break;
  463.         case kUserItem_AddedList:
  464.             Point    theCell;
  465.             theCell.v = theCell.h = 0;
  466.             if ((sTheFSpecs->GetCount() != 0) && (LGetSelect(true, &theCell, sAddedListH))) {    
  467.                     // list is not empty & something is selected, so enable Remove & Remove All
  468.                 SetEnable(kStdButton_Remove, true);        // enable remove button
  469.                 SetEnable(kStdButton_RemoveAll, true);    // enable remove all button
  470.             }
  471.             FixButtons(theDialogP, ioParam);
  472.             return sfHookSetActiveOffset + kUserItem_AddedList;
  473.         break;
  474.         case sfHookNullEvent:
  475.                 // Key actions have to be done first, then rebuilding the list if necessary,
  476.                 // and lastly, fixing button states, because that depends on the outcome of
  477.                 // the first operations.
  478.             if (sKeyPressed > 0) {
  479.                 if (myData->whichFocused == kUserItem_AddedList) {
  480.                     DoListKey(sKeyPressed, sAddedListH);
  481.                 } else
  482.                     DoListKey(sKeyPressed, gToolboxList);
  483.                 sKeyPressed = 0;
  484.             } else if (sNeedToRebuild) {
  485.                 sNeedToRebuild = false;
  486.                 haveRebuilt = true;
  487.                 return sfHookRebuildList;
  488.             } else if (haveRebuilt) {
  489.                 haveRebuilt = false;
  490.                 EnsureSelection(myData->whichFocused);
  491.             } else if (onceThrough && needToFixButtons) {
  492.                 if (!tabKludge) {
  493.                     FixButtons(theDialogP, ioParam);
  494.                     needToFixButtons = false;
  495.                 } else if (myData->whichFocused == kUserItem_FileList)
  496.                     tabKludge = false;    // for some reason, it seems to take an extra cycle
  497.                                         // to update the Reply field after tabbing to the Toolbox List
  498.             }
  499.         break;
  500.         default:
  501.         break;
  502.     }
  503.     return item;
  504. }
  505. // --------------------------------------------------------------------------------------------
  506. //    EnsureSelection
  507. // --------------------------------------------------------------------------------------------
  508. void UGetMultipleFiles::EnsureSelection(short inActiveList)
  509. {
  510.     ListHandle activeListH = (inActiveList == kUserItem_FileList) ? gToolboxList : sAddedListH;
  511.     Cell theCell = {0, 0};
  512.     if (!LGetSelect(true, &theCell, activeListH))
  513.         LSetSelect(true, theCell, activeListH);
  514. }
  515.  
  516. // --------------------------------------------------------------------------------------------
  517. //    MyDrawListItem
  518. //         This routine was taken from the Apple Macintosh Developer Technical Support
  519. //            Standard File Application, which I downloaded from Apple's web site, and modified.
  520. // --------------------------------------------------------------------------------------------
  521. pascal void UGetMultipleFiles::MyDrawListItem (WindowPtr theWindow, short item)
  522. {
  523.     Rect        itemRect;
  524.     Handle        itemHandle;
  525.     PenState    myPenState;            // current status of pen
  526.  
  527.     if (item == kUserItem_AddedList) {
  528.         LUpdate((*theWindow).visRgn, sAddedListH);
  529.         GetPenState(&myPenState);        // store pen state
  530.         GetDialogItem((DialogPtr) theWindow, item, userItem, &itemHandle, &itemRect);
  531.         PenSize(1, 1);                    // set pen to 1 pixel
  532.         InsetRect(&itemRect, -1, -1);    // adjust rectangle for framing
  533.         FrameRect(&itemRect);            // draw border
  534.         SetPenState(&myPenState);        // restore old pen state
  535.     }
  536. }
  537.  
  538. // --------------------------------------------------------------------------------------------
  539. //    MyCreateTextListInDialog
  540. //         This routine was taken from Inside Macintosh:More Macintosh Toolbox,
  541. //        List Manager chapter.
  542. // --------------------------------------------------------------------------------------------
  543. ListHandle UGetMultipleFiles::MyCreateTextListInDialog (DialogPtr myDialog, short myItemNumber)
  544. {
  545.     const short kTextLDEF = 0;    // resource ID of default LDEF
  546.  
  547.     Rect    myUserItemRect;    // enclosure of user item
  548.     Handle    myUserItemHdl;    // for GetDialogItem
  549.  
  550.     GetDialogItem(myDialog, myItemNumber, userItem, 
  551.                         &myUserItemHdl, &myUserItemRect);
  552.     return MyCreateVerticallyScrollingList(myDialog, myUserItemRect,
  553.                                                   1, kTextLDEF);
  554. }
  555.  
  556. // --------------------------------------------------------------------------------------------
  557. //    MyCreateVerticallyScrollingList
  558. //         This routine was taken from Inside Macintosh:More Macintosh Toolbox,
  559. //        List Manager chapter.
  560. // --------------------------------------------------------------------------------------------
  561. ListHandle UGetMultipleFiles::MyCreateVerticallyScrollingList (WindowPtr myWindow, Rect myRect,
  562.                                              short columnsInList, short myLDEF)
  563. {
  564.     const short kDoDraw = TRUE;            // always draw list after changes
  565.     const short kNoGrow = FALSE;            // don't leave room for size box
  566.     const short kIncludeScrollBar = TRUE;    // leave room for scroll bar
  567.     const short kScrollBarWidth = 15;        // width of vertical scroll bar
  568.  
  569.     Rect    myDataBounds;            // initial dimensions of the list
  570.     Point    myCellSize;                // size of each cell in list
  571.  
  572.     // specify dimensions of the list
  573.     // start with a list that contains no rows
  574.     SetRect(&myDataBounds, 0, 0, columnsInList, 0);
  575.  
  576.     // let the List Manager calculate the size of a cell
  577.     SetPt(&myCellSize, 0, 0);
  578.  
  579.     // adjust the rectangle to leave room for the scroll bar
  580.     myRect.right = myRect.right - kScrollBarWidth;
  581.  
  582.     // create the list
  583.     return LNew(&myRect, &myDataBounds, myCellSize, myLDEF, myWindow,
  584.               kDoDraw, kNoGrow, !kIncludeScrollBar, kIncludeScrollBar);
  585. }
  586.  
  587. // --------------------------------------------------------------------------------------------
  588. //    AddOneToList - Add the currently selected file to the list.  This can be done only because
  589. //        the Standard File Package fills in the reply record before it calls the dialog hook
  590. //        function (a point not well-documented in IM).  We made the "Open" button our "Add"
  591. //        button so that this would occur for us.  (The dialog hook has extracted the fields
  592. //        of the reply record for us already)
  593. // --------------------------------------------------------------------------------------------
  594. void UGetMultipleFiles::AddOneToList(Str255 name, long theDir, short theVRef)
  595. {
  596.     Point    theCell;
  597.     FSSpec    theFSSpec;
  598.     ArrayIndexT    foundAt;    // the index in the FSSpec array of this file, if present already
  599.     ArrayIndexT    putAt;        // where in the FSSpec array to put this file
  600.     char    theName[256];
  601.     
  602.     LString::CopyPStr((unsigned char *)name, (unsigned char *)theFSSpec.name);
  603.     theFSSpec.parID = theDir;
  604.     theFSSpec.vRefNum = theVRef;
  605.     theCell.v = theCell.h = 0;
  606.     
  607.     // First, check to see if the file is already present in list    
  608.     foundAt = sTheFSpecs->FetchIndexOf(&theFSSpec);
  609.     if (foundAt == sTheFSpecs->index_Bad) {            // if it's not already present
  610.         putAt = sTheFSpecs->FetchInsertIndexOf(&theFSSpec);        // figure out where to put it, based on the FSSpec array
  611.         sTheFSpecs->InsertItemsAt(1, putAt, &theFSSpec);        // insert it into the FSSpec array
  612.                                                                 // set the text of this new row to be the name of the file
  613.         theCell.v = LAddRow (1, (short) putAt-1, sAddedListH);    // insert a blank row into the displayed list for this file
  614.         LString::CopyPStr((unsigned char *)theFSSpec.name, (unsigned char *)theName);
  615.         if (!IsFile(theFSSpec)) {    // it's a folder or a volume
  616.             if (theFSSpec.parID != fsRtParID)    // if it's a folder (not a volume)
  617.                 LString::AppendPStr((unsigned char *)theName, "\p:", 256);
  618.             else {
  619.                 char temp[256];
  620.                 LString::CopyPStr("\p[", (unsigned char *)temp, 256);
  621.                 LString::AppendPStr((unsigned char *)temp, (unsigned char *)theName, 256);
  622.                 LString::AppendPStr((unsigned char *)temp, "\p]", 256);
  623.                  LString::CopyPStr((unsigned char *)temp, (unsigned char *)theName, 256);
  624.             }
  625.         }
  626.         LSetCell (theName, sizeof(char) * (strlen(p2cstr((unsigned char *)theName))+1), theCell, sAddedListH);
  627.                                                                 // set the text of this new row to be the name of the file
  628.     }
  629.     // set the button states
  630.     theCell.v = theCell.h = 0;
  631.     if ((sTheFSpecs->GetCount() != 0) && (LGetSelect(true, &theCell, sAddedListH))) {
  632.                     // list is not empty & something is selected, so enable Remove
  633.         SetEnable(kStdButton_Remove, true);        // enable remove button
  634.     }
  635.     if (sTheFSpecs->GetCount() != 0) {
  636.                     // list is not empty, so enable Remove All
  637.         SetEnable(kStdButton_RemoveAll, true);    // enable remove all button
  638.     }
  639. }
  640.  
  641. // --------------------------------------------------------------------------------------------
  642. //    RemoveFromList - Remove all files in the current selection from the list, leaving the
  643. //        selection on the item just after the last one that was deleted, or at the end of the
  644. //        remaining list if the bottom cell was selected and removed.
  645. // --------------------------------------------------------------------------------------------
  646. void UGetMultipleFiles::RemoveFromList()
  647. {
  648.     Point    theCell;
  649.     short    lastCell;
  650.  
  651.     theCell.v = theCell.h = 0;
  652.     while (LGetSelect(true, &theCell, sAddedListH)) {    // for each selected cell
  653.         lastCell = theCell.v;
  654.         (theCell.v)++;
  655.     }                        // lastCell is now the cell number of the last selected cell in the list
  656.     theCell.v = theCell.h = 0;
  657.     while (LGetSelect(true, &theCell, sAddedListH)) {    // for each selected cell
  658.         sTheFSpecs->RemoveItemsAt(1, theCell.v + 1);    // remove this item from the FSSpec array
  659.         LDelRow(1,  theCell.v, sAddedListH);            // remove this item from the displayed list
  660.         lastCell--;
  661.     }
  662.     if (sTheFSpecs->GetCount() == 0) {    // list is empty, so disable Remove & Remove All
  663.         SetEnable(kStdButton_Remove, false);        // disable remove button
  664.         SetEnable(kStdButton_RemoveAll, false);        // disable remove all button
  665.     } else {        // set selection at the cell that was below the last one deleted, or
  666.                     // on the last one remaining in the list
  667.         theCell.v = lastCell + 1;
  668.         LSetSelect(true, theCell, sAddedListH);
  669.     }
  670. }
  671.  
  672. // --------------------------------------------------------------------------------------------
  673. //    AddAllToList - Add all items in the current directory to the list.
  674. //        This has to be accomplished in a roundabout way, because we can't directly get a list
  675. //        of the files showing in the SF list.  So instead, we look at each file in the current
  676. //        directory, check it against out typelist, and add it if it has a good type.
  677. // --------------------------------------------------------------------------------------------
  678. void UGetMultipleFiles::AddAllToList(Boolean atDesktop)
  679. {
  680.     OSErr myErr = noErr;
  681.     FSSpecPtr items = new FSSpec[kMaxNumFilesToFind];
  682.     short    maxToGet = kMaxNumFilesToFind;
  683.     short    numFound;
  684.     short    itemIndex = 1;        // start at first entry in directory
  685.     Boolean    typeFound;
  686.     
  687.     while( myErr == noErr ) {    // while there are files to get from the directory
  688.         myErr = GetDirItems(GetSFVRefNum(),
  689.                                 GetSFCurDir(),
  690.                                 nil,
  691.                                 true,
  692.                                 true,
  693.                                 items,
  694.                                 maxToGet,
  695.                                 &numFound,
  696.                                 &itemIndex);    // itemIndex returns pointing to the next
  697.                                                     // directory entry to be found, next time through
  698.         switch (myErr) {
  699.             case noErr:    // This error code means 'repeat WHILE loop again, getting 
  700.                             // the next batch of names'.  This process will continue, adding ALL 
  701.                             // names until whole directory has been traversed.  Thus, we want to
  702.                             // perform the same task as the fnfErr condition, and then the WHILE
  703.                             // loop will repeat to get the next batch of names.
  704.  
  705.             case fnfErr:        // this error means it worked.
  706.             {
  707.                 for (short i=0; i<=numFound-1; i++) {    // for each file, check it's type and add
  708.                                                                     // it to the list if appropriate.
  709.                     FInfo        theFinderInfo;
  710.                     FSSpec        theFSpec;
  711.                     OSErr        myErr;
  712.         
  713.                     LString::CopyPStr((unsigned char *)(items[i]).name, (unsigned char *)theFSpec.name);
  714.                     theFSpec.parID = (items[i]).parID;
  715.                     theFSpec.vRefNum = (items[i]).vRefNum;
  716.                     myErr = FSpGetFInfo((const FSSpec *) &theFSpec, &theFinderInfo);
  717.  
  718.                     typeFound = TypeInList( theFinderInfo.fdType );
  719.  
  720.                                 // if theFSSpec isn't in the exclusion list, or there is no list
  721.                     if (sNixFiles == nil || (sNixFiles->FetchIndexOf(&theFSpec) == sNixFiles->index_Bad)) {
  722.                         if ((typeFound) || (sNumTypes == -1)) {
  723.                             AddOneToList(theFSpec.name, theFSpec.parID, theFSpec.vRefNum);
  724.                         }
  725.                     }
  726.                 }
  727.             }
  728.             break;
  729.             default:
  730.                 Throw_(err_otherGetDirErr);
  731.             break;
  732.         }
  733.     }    // end while myErr == noErr
  734.     if (atDesktop) {
  735.         // then find FSSpec for each volume (?) and add it to the list, if it's not already present
  736.         HParamBlockRec pb;
  737.         Str31    volName;
  738.         FSSpec    theVSpec;    // we're going to pretend that we can treat a volume like a file
  739.         short numVolume = 1;
  740.         myErr = noErr;
  741.         while (myErr == noErr) {    // for each volume:
  742.             pb.volumeParam.ioVolIndex = numVolume++;
  743.             pb.volumeParam.ioNamePtr = volName;
  744.             myErr = PBHGetVInfoSync(&pb);
  745.             if (myErr == noErr) {    // then add it to the list
  746.                 LString::CopyPStr((unsigned char *)volName, (unsigned char *)theVSpec.name);
  747.                 theVSpec.vRefNum = pb.volumeParam.ioVRefNum;
  748.                 theVSpec.parID = fsRtParID;    // this is the parent ID of the root directory (a convenient fiction), and will serve to denote the whole volume
  749.  
  750.                             // if theVSpec isn't in the exclusion list, or there is no list
  751.                 if (sNixFiles == nil || (sNixFiles->FetchIndexOf(&theVSpec) == sNixFiles->index_Bad)) {
  752.                     AddOneToList(theVSpec.name, theVSpec.parID, theVSpec.vRefNum);
  753.                 }
  754.             }
  755.         }
  756.         if (myErr == nsvErr) {    // this error means we got all the volumes!
  757.             // do nothing - we're happy
  758.         }
  759.     }
  760.     delete[] items;
  761. }
  762.  
  763. // --------------------------------------------------------------------------------------------
  764. //    RemoveAllFromList - Remove all files from our list.
  765. // --------------------------------------------------------------------------------------------
  766. void UGetMultipleFiles::RemoveAllFromList()
  767. {
  768.     Point    theCell;
  769.  
  770.     theCell.v = theCell.h = 0;
  771.     
  772.     while (sTheFSpecs->GetCount() != 0) {        // as long as there's a cell left
  773.         theCell.v = sTheFSpecs->GetCount() - 1;            // pick the last cell
  774.         sTheFSpecs->RemoveItemsAt(1, theCell.v + 1);    // remove this thing from the FSSpec array
  775.         LDelRow(1,  theCell.v, sAddedListH);             // remove it from the displayed list
  776.     }
  777.     if (sTheFSpecs->GetCount() == 0) {    // list is empty, so disable Remove & Remove All
  778.         SetEnable(kStdButton_Remove, false);        // disable remove button
  779.         SetEnable(kStdButton_RemoveAll, false);        // disable remove all button
  780.     } else {        // set selection on the last one remaining in the list
  781.         theCell.v = sTheFSpecs->GetCount() - 1;    //  (we should never be here)
  782.         LSetSelect(true, theCell, sAddedListH);
  783.     }
  784. }
  785.  
  786. // --------------------------------------------------------------------------------------------
  787. //    Utility functions
  788. // --------------------------------------------------------------------------------------------
  789. long UGetMultipleFiles::GetSFCurDir(void)
  790. {
  791.     return *((long *)0x398);
  792. }
  793.  
  794. short UGetMultipleFiles::GetSFVRefNum(void)
  795. {
  796.     return -1 * (*((short *)0x214));
  797. }
  798.  
  799. void UGetMultipleFiles::SetCurrentVolume(short vRefNum)
  800. {
  801.     (*((short *)0x214)) = -vRefNum;
  802. }
  803.  
  804. void UGetMultipleFiles::SetCurrentDirectory(long dirID)
  805. {
  806.     *((long *)0x398) = dirID;
  807. }
  808.  
  809. void UGetMultipleFiles::SetEnable(ResIDT theItem, Boolean enabled)
  810. {
  811.     Rect    itemRect;
  812.     Handle    itemHandle;
  813.  
  814.     GetDialogItem(sDialogP, theItem, btnCtrl, &itemHandle, &itemRect);
  815.  
  816.     if (enabled)
  817.         HiliteControl((ControlHandle)itemHandle, 0);
  818.     else
  819.         HiliteControl((ControlHandle)itemHandle, 255);
  820. }
  821.  
  822.  
  823. Boolean UGetMultipleFiles::GetEnabled(ResIDT theItem)
  824. {
  825.     Rect    itemRect;
  826.     Handle    itemHandle;
  827.  
  828.     GetDialogItem(sDialogP, theItem, btnCtrl, &itemHandle, &itemRect);
  829.  
  830.     return ((**((ControlHandle)itemHandle)).contrlHilite < kControlDisabledPart);
  831. }
  832.  
  833. // --------------------------------------------------------------------------------------------
  834. //    fixButtons - Sets enabled/disabled states, and default, based on selections & lists
  835. // --------------------------------------------------------------------------------------------
  836. void UGetMultipleFiles::FixButtons (DialogPtr theDialogP, void *ioParam)
  837. {
  838.     static DialogPeek theDP = (DialogPeek) theDialogP;
  839.     Boolean            topEmpty = ((**gToolboxList).dataBounds.bottom == 0);
  840.     Boolean            botEmpty = (sTheFSpecs->GetCount() == 0);
  841.     Rect            itemRect;
  842.     Handle            itemHandle;
  843.     RGBColor        bgColor, fgSaveColor;
  844.     PenState        curPen;
  845.     short            dataSize = sizeof(Str63);
  846.     Cell            curSelCell = {0,0};
  847.     Boolean            toolboxActive = ((dataHolderType *)ioParam)->whichFocused == kUserItem_FileList;
  848.     
  849.     SetEnable(kStdButton_RemoveAll, !botEmpty);    // enable remove all button if added list isn't empty
  850.     SetEnable(kStdButton_Remove, !botEmpty && !toolboxActive);    // enable remove button if added list is active and non-empty
  851.     SetEnable(kStdButton_AddAll, !topEmpty);    // enable add all if top list isn't empty
  852.     if (sAddFolders)
  853.         SetEnable(kStdButton_Select, (!topEmpty) && toolboxActive);    // enable add all if top list is active and non-empty
  854.     SetEnable(kStdButton_Add, (!topEmpty) && toolboxActive);    // enable add all if top list is active and non-empty
  855.  
  856.     if ( sAddFolders && toolboxActive) {
  857.         // set text for folder button:
  858.         GetDialogItem(theDialogP, kStdButton_Select, btnCtrl, &itemHandle, &itemRect);
  859.         if (topEmpty) {    // if there are no entries in the top list
  860.             ::SetControlTitle((ControlHandle) itemHandle, "\pNothing to Add");
  861.             ::ValidRect(&itemRect);
  862.         } else {
  863.             SetButtonTitle(itemHandle, sReply.sfFile.name, itemRect);
  864.         }
  865.     }
  866.     // if top list is empty, and bottom list isn't, then set default button to Done
  867.     if (topEmpty && !botEmpty) {
  868.         if (theDP->aDefItem != kStdButton_Done) {
  869.             theDP->aDefItem = kStdButton_Done;
  870.             GetDialogItem(theDialogP, kStdButton_Done, btnCtrl, &itemHandle, &itemRect);
  871.             InsetRect(&itemRect, -4, -4);
  872.             InvalRect(&itemRect);
  873.             GetDialogItem(theDialogP, sfItemOpenButton, btnCtrl, &itemHandle, &itemRect);
  874.             InsetRect(&itemRect, -4, -4);
  875.             GetPenState(&curPen);
  876.             PenSize(3, 3);
  877.             GetBackColor(&bgColor);
  878.             GetForeColor(&fgSaveColor);
  879.             RGBForeColor(&bgColor);
  880.             FrameRoundRect(&itemRect, 16, 16);
  881.             RGBForeColor(&fgSaveColor);
  882.             SetPenState(&curPen);
  883.         }
  884.     } else {
  885.         if (theDP->aDefItem != sfItemOpenButton) {
  886.             theDP->aDefItem = sfItemOpenButton;
  887.             GetDialogItem(theDialogP, sfItemOpenButton, btnCtrl, &itemHandle, &itemRect);
  888.             InsetRect(&itemRect, -4, -4);
  889.             InvalRect(&itemRect);
  890.             GetDialogItem(theDialogP, kStdButton_Done, btnCtrl, &itemHandle, &itemRect);
  891.             InsetRect(&itemRect, -4, -4);
  892.             GetPenState(&curPen);
  893.             PenSize(3, 3);
  894.             GetBackColor(&bgColor);
  895.             GetForeColor(&fgSaveColor);
  896.             RGBForeColor(&bgColor);
  897.             FrameRoundRect(&itemRect, 16, 16);
  898.             RGBForeColor(&fgSaveColor);
  899.             SetPenState(&curPen);
  900.         }
  901.     }
  902. }
  903.  
  904. // Utility by TE
  905.  
  906. Boolean UGetMultipleFiles::TypeInList( OSType inType )
  907. {
  908.     if( sNumTypes == -1 ) 
  909.         return true;
  910.         
  911.     for (short j=0; j <= sNumTypes; j++) 
  912.     {    // for each type in the list:
  913.         if (inType == sTypeList[j])    // is this file's type one in the list?
  914.                 return true;
  915.     }
  916.     return false;
  917. }
  918.  
  919. // Taken from IM:Files pg 3-35
  920. void UGetMultipleFiles::SetButtonTitle (Handle ButtonHdl, Str255 name, Rect ButtonRect)
  921. {
  922.     short    result, width;
  923.     LStr255 finalStr;
  924.     
  925.     width = (ButtonRect.right - ButtonRect.left) - (StringWidth(kFolderButtonRoot) + CharWidth(' '));
  926.     result = TruncString(width, name, smTruncMiddle);
  927.     finalStr = kFolderButtonRoot;
  928.     finalStr.Append(name);
  929.     finalStr.Append('"');
  930.     ::SetControlTitle((ControlHandle) ButtonHdl, finalStr);
  931.     ::ValidRect(&ButtonRect);
  932. }
  933.  
  934. void UGetMultipleFiles::ReplaceString ( Str255 destStr, Str255 whatStr, Str255 byStr ) 
  935. {
  936.     Handle    tH;
  937.     long        newOffset;
  938.     short        len;
  939.     
  940.     tH = NewHandle(512L);
  941.     BlockMove((Ptr) destStr, *tH, destStr[0]+1);
  942.     newOffset = Munger(tH, 0L, &whatStr[1], whatStr[0], &byStr[1], byStr[0]);
  943.     if (newOffset > 0) {
  944.         len = destStr[0];
  945.         BlockMove(*tH, (Ptr) destStr, 255);
  946.         len = len + byStr[0] - whatStr[0];
  947.         if (len > 255)
  948.             len = 255;
  949.         destStr[0] = len;
  950.     }
  951.     DisposeHandle(tH);
  952. }
  953.  
  954. void UGetMultipleFiles::DoListKey(short theKey, ListHandle theListH)
  955. {
  956.     Cell theCell = {0,0};
  957.     short numVisible = (**theListH).visible.bottom - (**theListH).visible.top;
  958.     
  959.     switch (theKey) {
  960.         case kHome:
  961.             if (LGetSelect(true, &theCell, theListH)) {
  962.                 LSetSelect(false, theCell, theListH);
  963.                 theCell.v = 0;
  964.                 LSetSelect(true, theCell, theListH);
  965.                 LAutoScroll(theListH);
  966.             }
  967.         break;
  968.         case kEnd:
  969.             if (LGetSelect(true, &theCell, theListH)) {
  970.                 LSetSelect(false, theCell, theListH);
  971.                 theCell.v = (**theListH).dataBounds.bottom-1;
  972.                 LSetSelect(true, theCell, theListH);
  973.                 LAutoScroll(theListH);
  974.             }
  975.         break;
  976.         case kUpArrow:
  977.             if (theListH != gToolboxList) {
  978.                 if (LGetSelect(true, &theCell, theListH)) {
  979.                     LSetSelect(false, theCell, theListH);
  980.                     if (theCell.v > 0)
  981.                         (theCell.v)--;
  982.                     LSetSelect(true, theCell, theListH);
  983.                     LAutoScroll(theListH);
  984.                 }
  985.             }
  986.         break;
  987.         case kDownArrow:
  988.             if (theListH != gToolboxList) {
  989.                 if (LGetSelect(true, &theCell, theListH)) {
  990.                     LSetSelect(false, theCell, theListH);
  991.                     if (theCell.v < (**theListH).dataBounds.bottom-1)
  992.                         (theCell.v)++;
  993.                     LSetSelect(true, theCell, theListH);
  994.                     LAutoScroll(theListH);
  995.                 }
  996.             }
  997.         break;
  998.         case kPageUp:
  999.             if (LGetSelect(true, &theCell, theListH)) {
  1000.                 LSetSelect(false, theCell, theListH);
  1001.                 if (theCell.v > numVisible-1)
  1002.                     theCell.v -= numVisible;
  1003.                 else
  1004.                     theCell.v = 0;
  1005.                 LSetSelect(true, theCell, theListH);
  1006.                 LAutoScroll(theListH);
  1007.             }
  1008.         break;
  1009.         case kPageDown:
  1010.             if (LGetSelect(true, &theCell, theListH)) {
  1011.                 LSetSelect(false, theCell, theListH);
  1012.                 if (theCell.v < (**theListH).dataBounds.bottom - numVisible)
  1013.                     theCell.v += numVisible;
  1014.                 else
  1015.                     theCell.v = (**theListH).dataBounds.bottom-1;
  1016.                 LSetSelect(true, theCell, theListH);
  1017.                 LAutoScroll(theListH);
  1018.             }
  1019.         break;
  1020.     }
  1021.     if (theListH == gToolboxList)
  1022.         needToFixButtons = true;
  1023. }
  1024.  
  1025. Boolean UGetMultipleFiles::IsFile(FSSpec inFile)
  1026. {
  1027.     CInfoPBRec pb;
  1028.     OSErr error;
  1029.     
  1030.     error = GetCatInfoNoName(inFile.vRefNum, inFile.parID, inFile.name, &pb);
  1031.     if ( error == noErr )
  1032.         if ( (pb.dirInfo.ioFlAttrib & ioDirMask) != 0 )
  1033.             return false;
  1034.     return true;
  1035.     //handle error in GetCatInfoNoName here FIX
  1036. }
  1037.  
  1038. // --------------------------------------------------------------------------------------------
  1039. //    GetCatInfoNoName - Taken from MoreFiles 1.4.5
  1040. // --------------------------------------------------------------------------------------------
  1041. OSErr UGetMultipleFiles::GetCatInfoNoName(short vRefNum,
  1042.                                long dirID,
  1043.                                ConstStr255Param name,
  1044.                                CInfoPBPtr pb)
  1045. {
  1046.     Str31 tempName;
  1047.     OSErr error;
  1048.     
  1049.     /* Protection against File Sharing problem */
  1050.     if ( (name == NULL) || (name[0] == 0) )
  1051.     {
  1052.         tempName[0] = 0;
  1053.         pb->dirInfo.ioNamePtr = tempName;
  1054.         pb->dirInfo.ioFDirIndex = -1;    /* use ioDirID */
  1055.     }
  1056.     else
  1057.     {
  1058.         pb->dirInfo.ioNamePtr = (StringPtr)name;
  1059.         pb->dirInfo.ioFDirIndex = 0;    /* use ioNamePtr and ioDirID */
  1060.     }
  1061.     pb->dirInfo.ioVRefNum = vRefNum;
  1062.     pb->dirInfo.ioDrDirID = dirID;
  1063.     error = PBGetCatInfoSync(pb);
  1064.     pb->dirInfo.ioNamePtr = NULL;
  1065.     return ( error );
  1066. }
  1067.